/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package TimeRecording.business;

import TimeRecording.business.calcs.DailyAllocation;
import TimeRecording.enums.PartOfDay;
import TimeRecording.tables.Employee;
import TimeRecording.tables.HolidayAdjustment;
import TimeRecording.tables.HolidayRequest;
import TimeRecording.tables.Session;
import TimeRecording.tables.SicknessReport;
import TimeRecording.tables.WeeklyTime;
import TimeRecording.tables.WorkingPattern;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import javax.persistence.EntityManager;

/**
 * This class holds details of all holidays, sickness, hours expected, hours
 * worked, etc. for a specified period of time in a "day-centric" manner.  That
 * is, the class holds a collection of "DailyAllocation" objects where each
 * "DailyAllocation" contains the number of hours, sickness, holiday, etc. for
 * that specific day.
 *
 * Note that patterns for flexi and semi-flexi are not smoothed out by this
 * class.  It merely plumbs in details exactly as they are in the patterns.
 *
 * @author dpatchett
 */
public class DailyPeriodData extends RawPeriodData {

    // Our internal representation of how days have been split up
    private ArrayList<DailyAllocation> days;

    public DailyPeriodData() {
        super();
    }

    public DailyPeriodData(
            EntityManager entityManager,
            Employee employee,
            DateRange dateRange) {

        super(entityManager, employee, dateRange);
    }

    protected void refreshData(EntityManager entityManager,
            Employee employee, DateRange dateRange) {

        super.employee = employee;
        super.dateRange = dateRange;
        refreshData(entityManager);
    }

    public ArrayList<DailyAllocation> getDailyInformation() {
        return days;
    }

    /**
     * This method refreshes the basic data in the supporting raw-data class
     * and then processes those files to come up with a new daily-centric view.
     * 
     * @param entityManager
     */
    @Override
    protected void refreshData(EntityManager entityManager) {

        super.refreshData(entityManager);
        processData();
    }

    /**
     * This method processes the raw data in the underlying raw data collection
     * in order to come up with a new daily-centric view of the data.
     */
    public void processData() {

//        System.out.print("Process Data ");
//        System.out.print(
//                startCal.get(GregorianCalendar.DAY_OF_MONTH) + "/" +
//                startCal.get(GregorianCalendar.MONTH) + "/" +
//                startCal.get(GregorianCalendar.YEAR));
//        System.out.print(" to ");
//        System.out.println(
//                stopCal.get(GregorianCalendar.DAY_OF_MONTH) + "/" +
//                stopCal.get(GregorianCalendar.MONTH) + "/" +
//                stopCal.get(GregorianCalendar.YEAR));

        GregorianCalendar start = (GregorianCalendar)startCal.clone();
        GregorianCalendar stop = (GregorianCalendar)stopCal.clone();

        days = new ArrayList<DailyAllocation>();

        while(!start.after(stop)) {

            int dayOfWeek = start.get(GregorianCalendar.DAY_OF_WEEK);

            DailyAllocation day = new DailyAllocation();
            day.setDate(start.getTime());

            processPatterns(day, dayOfWeek);
            processSessions(day);

            processHolidayRequests(day);
            processHolidayAdjustments(day);
            processSicknessReports(day);

            processWeeklyTimes(start, day);

            days.add(day);
            start.add(GregorianCalendar.DATE, 1);
        }
    }

    private void processPatterns(DailyAllocation day, int dayOfWeek) {

        for (WorkingPattern pattern : workingPatterns) {

            if (!pattern.isActive(day.getDate())) {
                continue;
            }

            switch (pattern.getPatternType()) {

                case CLOCKING_TIME:
                    break;

                case DAILY_PAID_TIME:
                    day.setFixedPattern(true);
                    if (pattern.getDay(dayOfWeek)) {
                        day.getFixed().adjustExpected(pattern.getDailyHours());
                    }
                    break;

                case FLEXI_PAID_TIME:
                    day.setFlexiPattern(true);
                    if (pattern.getDay(dayOfWeek)) {
                        day.getFlexi().adjustExpected(pattern.getDailyHours());
                    }
                    break;

                case WEEKLY_PAID_TIME:
                    day.setSemiFlexiPattern(true);
                    if (pattern.getDay(dayOfWeek)) {
                        day.getSemiFlexi().adjustExpected(pattern.getDailyHours());
                    }
                    break;

                case SESSIONAL_TIME:
                    day.setSessionalPattern(true);
                    if (pattern.getDay(dayOfWeek)) {
                        day.getSessional().adjustExpected(pattern.getDailyHours());
                    }
                    break;
                case AD_HOC_TIME:
                    // There is NEVER expected ad hoc time
                    day.setAdHocPattern(true);
                    break;
            }
        }
    }

    private void processSessions(DailyAllocation day) {

        GregorianCalendar cal = new GregorianCalendar();

        for (Session session : sessions) {

            // Session not for today?
            cal.setTime(session.getStartTime());
            DaisyChainUtilities.cleanCalendar(cal);
            if (!cal.getTime().equals(day.getDate())) {
                continue;
            }

            day.getSessional().adjustExpected(session.getHours());
        }
    }

    /**
     * Strip out holiday request details and rack stuff up against the day.
     * Multiple holidays should not be allowed to clash.  Thus, if we encounter
     * a second holiday for the same day then we're assuming we have two half
     * days which equates to one full day.
     *
     * @param day
     */
    private void processHolidayRequests(DailyAllocation day) {

        for (HolidayRequest holiday : holidayRequests) {
            PartOfDay partOfDay = holiday.isActive(day.getDate());
            if (partOfDay != null) {
                if (day.getHoliday().getPartOfDay() == null) {
                    day.getHoliday().setPartOfDay(partOfDay);
                } else {
                    day.getHoliday().setPartOfDay(PartOfDay.ALL_DAY);
                }
            }
        }

        for (HolidayRequest holiday : unapprovedHolidayRequests) {
            PartOfDay partOfDay = holiday.isActive(day.getDate());
            if (partOfDay != null) {
                if (day.getUnapprovedHoliday().getPartOfDay() == null) {
                    day.getUnapprovedHoliday().setPartOfDay(partOfDay);                    
                } else {                                                
                    day.getUnapprovedHoliday().setPartOfDay(PartOfDay.ALL_DAY);                                         
                }
            }
        }
    }
    
    private void processHolidayAdjustments(DailyAllocation day) {

        for (HolidayAdjustment holidayAdjustment : holidayAdjustments) {
            if (holidayAdjustment.getAdjustmentDate().equals(day.getDate())) {
                day.getHoliday().adjustAdjustment(
                        holidayAdjustment.getHoursAdjusted());
            }
        }

        for (HolidayAdjustment holidayAdjustment : unapprovedHolidayAdjustments) {
            if (holidayAdjustment.getAdjustmentDate().equals(day.getDate())) {
                day.getUnapprovedHoliday().adjustAdjustment(
                        holidayAdjustment.getHoursAdjusted());
            }
        }
    }

    private void processSicknessReports(DailyAllocation day) {

        for (SicknessReport sicknessReport : sicknessReports) {
            PartOfDay partOfDay = sicknessReport.isActive(day.getDate());
            if (partOfDay != null) {
                if (day.getSickness().getPartOfDay() == null) {
                    day.getSickness().setPartOfDay(partOfDay);
                } else {
                    day.getSickness().setPartOfDay(PartOfDay.ALL_DAY);
                }
            }
        }

        for (SicknessReport sicknessReport : unapprovedSicknessReports) {
            PartOfDay partOfDay = sicknessReport.isActive(day.getDate());
            if (partOfDay != null) {
                if (day.getUnapprovedSickness().getPartOfDay() == null) {
                    day.getUnapprovedSickness().setPartOfDay(partOfDay);
                } else {
                    day.getUnapprovedSickness().setPartOfDay(PartOfDay.ALL_DAY);
                }
            }
        }
    }

    private void processWeeklyTimes(GregorianCalendar date, DailyAllocation day) {

        WeeklyTime weeklyTime = getWeeklyTime(date);

        // Any timesheet details?

        if (weeklyTime == null) {
            day.setClocked(0.0);
            
        } else {

            // Anything been entered in the timesheet?

            if (weeklyTime.getEntered(date) != null &&
                    weeklyTime.getEntered(date) != 0.0) {
                day.setClocked(weeklyTime.getEntered(date));

            // Anything recorded on the clock?

            }  else if (weeklyTime.getClocked(date) != null &&
                    weeklyTime.getClocked(date) != 0.0) {
                day.setClocked(weeklyTime.getClocked(date));

            } else {
                day.setClocked(0.0);
            }
        }
    }
}
